/**
 *  Copyright 2010 Kyle O'Connor, Justin Arling, Mike Mercieca
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package edu.drexel.sd0910.ece01.aqmon.manager.xbee;

import edu.drexel.sd0910.ece01.aqmon.util.OSUtils;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.HashSet;

import org.apache.log4j.Logger;

/**
 * This class returns the most likely COM port that the XBee base station is
 * connected to. This class does not currently check the device by communicating
 * with it.
 * 
 * @author Kyle O'Connor
 * 
 */
public class XBeeAutoDetect implements SerialPortEventListener {

	private final static Logger log = Logger.getLogger(XBeeAutoDetect.class);

	/**
	 * Default constructor.
	 */
	public XBeeAutoDetect() {
	}

	/**
	 * Method to actually connect and communicate with each port to test if it is an XBee.
	 * @param baud baud rate
	 */
	@SuppressWarnings({ "unused", "unchecked" })
	@Deprecated
	private void autoDetectDevice(int baud) {
		Enumeration<CommPortIdentifier> ports = CommPortIdentifier.getPortIdentifiers();
		while (ports.hasMoreElements()) {
			final SerialPort port;
			final boolean found = false;
			final String portName = "";

			CommPortIdentifier portId = ports.nextElement();
			try {
				log.debug("Trying port " + portId.getName());
				port = (SerialPort) portId.open("AQMon - Autodetect", 2000);
				port
						.setSerialPortParams(baud, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
								SerialPort.PARITY_NONE);
				port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);

				final InputStream inputStream = port.getInputStream();
				final OutputStream outputStream = port.getOutputStream();

				new Thread() {
					public void run() {
						byte[] buffer = new byte[1024];
						int len = -1;
						try {
							while ((len = inputStream.read(buffer)) > -1) {
								String response = new String(buffer, 0, len);
								if (response.equals("8044")) {
									// found = true;
									// portName = port.getName();
									port.close();
									log.debug("Found XBee Device on " + portName);
									break;
								}
							}
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}.start();

				new Thread() {
					public void run() {
						try {
							outputStream.write("+++".getBytes());
							Thread.sleep(3000);
							outputStream.write("ATVR\r".getBytes());
							Thread.sleep(3000);
						} catch (IOException e) {
							e.printStackTrace();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						return;
					}
				}.start();

			} catch (Exception e) {
				// port.close();
				log.debug(portId.getName() + " didn't work (" + portId.getCurrentOwner() + ")");
				// if there was an error with any port then move onto the next
				continue;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gnu.io.SerialPortEventListener#serialEvent(gnu.io.SerialPortEvent)
	 */
	public void serialEvent(SerialPortEvent arg0) {


	}

	/**
	 * Testing auto detection.
	 */
	public static void main(String[] args) {
		System.out.println(new XBeeAutoDetect().getComPort());

	}

	// return the most likely com port, maybe
	@SuppressWarnings("unchecked")
	public String getComPort() throws RuntimeException {
		String retstr = null;
		String userdir = System.getProperty("java.io.tmpdir") + File.separator;
		if (OSUtils.isMac()) {
			// do a mac thing
		} else if (OSUtils.isUnix()) {
			// do a linux thing
		} else if (OSUtils.isWindows()) {
			// do a windows thing
			String commandSize[] = new String[] { "regedit", "/e", userdir + "tmp.reg",
					"HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Enum\\FTDIBUS" };
			String tag = "\"PortName\"=";
			HashSet<String> r1 = new HashSet<String>();
			try {
				Process process = Runtime.getRuntime().exec(commandSize);
				process.waitFor();
				BufferedReader r = new BufferedReader(new FileReader(userdir + "tmp.reg"));
				String s = trimNulls(r.readLine());
				while (null != s) {
					int p = s.indexOf(tag);
					if (p != -1)
						r1.add(s.substring(p + tag.length() + 1, s.length() - 1));
					s = trimNulls(r.readLine());
				}
				r.close();

			} catch (Exception e) {
				throw new RuntimeException(e.getMessage());
			}

			HashSet<String> r2 = new HashSet<String>();

			Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
			while (portList.hasMoreElements()) {
				CommPortIdentifier portId = portList.nextElement();
				if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
					r2.add(portId.getName());
				}
			}

			r1.retainAll(r2);// intersection of live ports and ftdi known ports

			String[] ports = r1.toArray(new String[0]);
			if (ports.length == 0) {
				throw new RuntimeException("No suitable com port found, is device connected?");
			}
			if (ports.length > 1) {
				String c = "";
				for (int x = 0; x < ports.length; x++)
					c = c + " " + ports[x];
				throw new RuntimeException("Too many possible Com ports found: " + c);
			}
			retstr = ports[0];
		}
		return retstr;
	}

	// weird null thing going on with regedit
	private String trimNulls(String s) {
		if (s == null)
			return null;
		String r = "";
		char[] t = s.toCharArray();
		for (int x = 0; x < t.length; x++) {
			if (t[x] != 0)
				r = r + t[x];
		}
		return r;
	}

}
